winsafe\gui\native_controls/
edit.rs

1use std::any::Any;
2use std::marker::PhantomPinned;
3use std::pin::Pin;
4use std::sync::Arc;
5
6use crate::co;
7use crate::decl::*;
8use crate::gui::{events::*, privs::*, *};
9use crate::msg::*;
10use crate::prelude::*;
11
12struct EditObj {
13	base: BaseCtrl,
14	events: EditEvents,
15	_pin: PhantomPinned,
16}
17
18native_ctrl! { Edit: EditObj => EditEvents;
19	/// Native
20	/// [edit](https://learn.microsoft.com/en-us/windows/win32/controls/about-edit-controls)
21	/// (text box) control.
22}
23
24impl Edit {
25	/// Instantiates a new `Edit` object, to be created on the parent window
26	/// with [`HWND::CreateWindowEx`](crate::HWND::CreateWindowEx).
27	///
28	/// # Panics
29	///
30	/// Panics if the parent window was already created – that is, you cannot
31	/// dynamically create an `Edit` in an event closure.
32	///
33	/// # Examples
34	///
35	/// ```no_run
36	/// use winsafe::{self as w, prelude::*, gui};
37	///
38	/// let wnd: gui::WindowMain; // initialized somewhere
39	/// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
40	///
41	/// let txt = gui::Edit::new(
42	///     &wnd,
43	///     gui::EditOpts {
44	///         position: gui::dpi(10, 10),
45	///         width: gui::dpi_x(120),
46	///         ..Default::default()
47	///     },
48	/// );
49	/// ```
50	#[must_use]
51	pub fn new(parent: &(impl GuiParent + 'static), opts: EditOpts) -> Self {
52		let ctrl_id = auto_id::set_if_zero(opts.ctrl_id);
53		let new_self = Self(Arc::pin(EditObj {
54			base: BaseCtrl::new(ctrl_id),
55			events: EditEvents::new(parent, ctrl_id),
56			_pin: PhantomPinned,
57		}));
58
59		let self2 = new_self.clone();
60		let parent2 = parent.clone();
61		parent
62			.as_ref()
63			.before_on()
64			.wm(parent.as_ref().wnd_ty().creation_msg(), move |_| {
65				self2.0.base.create_window(
66					opts.window_ex_style,
67					"EDIT",
68					Some(&opts.text),
69					opts.window_style | opts.control_style.into(),
70					opts.position.into(),
71					SIZE::new(opts.width, opts.height),
72					&parent2,
73				)?;
74				ui_font::set(self2.hwnd())?;
75				parent2
76					.as_ref()
77					.add_to_layout(self2.hwnd(), opts.resize_behavior)?;
78				Ok(0) // ignored
79			});
80
81		new_self.default_message_handlers(parent);
82		new_self
83	}
84
85	/// Instantiates a new `Edit` object, to be loaded from a dialog resource
86	/// with [`HWND::GetDlgItem`](crate::HWND::GetDlgItem).
87	///
88	/// # Panics
89	///
90	/// Panics if the parent dialog was already created – that is, you cannot
91	/// dynamically create an `Edit` in an event closure.
92	#[must_use]
93	pub fn new_dlg(
94		parent: &(impl GuiParent + 'static),
95		ctrl_id: u16,
96		resize_behavior: (Horz, Vert),
97	) -> Self {
98		let new_self = Self(Arc::pin(EditObj {
99			base: BaseCtrl::new(ctrl_id),
100			events: EditEvents::new(parent, ctrl_id),
101			_pin: PhantomPinned,
102		}));
103
104		let self2 = new_self.clone();
105		let parent2 = parent.clone();
106		parent.as_ref().before_on().wm_init_dialog(move |_| {
107			self2.0.base.assign_dlg(&parent2)?;
108			parent2
109				.as_ref()
110				.add_to_layout(self2.hwnd(), resize_behavior)?;
111			Ok(true) // ignored
112		});
113
114		new_self.default_message_handlers(parent);
115		new_self
116	}
117
118	fn default_message_handlers(&self, parent: &(impl GuiParent + 'static)) {
119		let self2 = self.clone();
120		let parent2 = parent.clone();
121		parent
122			.as_ref()
123			.before_on()
124			.wm_command(self.ctrl_id(), co::EN::CHANGE, move || {
125				// EN_CHANGE is first sent to the control before CreateWindowEx()
126				// returns, so if the user handles EN_CHANGE, the Edit HWND won't be
127				// set yet. So we set the HWND here.
128				if *self2.hwnd() == HWND::NULL {
129					let hctrl = parent2.as_ref().hwnd().GetDlgItem(self2.ctrl_id())?;
130					self2.0.base.set_hwnd(hctrl);
131				}
132				Ok(())
133			});
134	}
135
136	/// Hides any balloon tip by sending an
137	/// [`em::HideBalloonTip`](crate::msg::em::HideBalloonTip) message.
138	pub fn hide_balloon_tip(&self) -> SysResult<()> {
139		unsafe { self.hwnd().SendMessage(em::HideBalloonTip {}) }
140	}
141
142	/// Limits the number of characters that can be type by sending an
143	/// [`em::SetLimitText`](crate::msg::em::SetLimitText) message.
144	pub fn limit_text(&self, max_chars: Option<u32>) {
145		unsafe {
146			self.hwnd().SendMessage(em::SetLimitText { max_chars });
147		}
148	}
149
150	/// Sets the selection range of the text by sending an
151	/// [`em::SetSel`](crate::msg::em::SetSel) message.
152	///
153	/// # Examples
154	///
155	/// Selecting all text in the control:
156	///
157	/// ```no_run
158	/// use winsafe::{self as w, prelude::*, gui};
159	///
160	/// let my_edit: gui::Edit; // initialized somewhere
161	/// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
162	/// # let my_edit = gui::Edit::new(&wnd, gui::EditOpts::default());
163	///
164	/// my_edit.set_selection(0, -1);
165	/// ```
166	///
167	/// Clearing the selection:
168	///
169	/// ```no_run
170	/// use winsafe::gui;
171	///
172	/// let my_edit: gui::Edit; // initialized somewhere
173	/// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
174	/// # let my_edit = gui::Edit::new(&wnd, gui::EditOpts::default());
175	///
176	/// my_edit.set_selection(-1, -1);
177	/// ```
178	pub fn set_selection(&self, start: i32, end: i32) {
179		unsafe {
180			self.hwnd().SendMessage(em::SetSel { start, end });
181		}
182	}
183
184	/// Sets the text by calling
185	/// [`HWND::SetWindowText`](crate::HWND::SetWindowText).
186	pub fn set_text(&self, text: &str) -> SysResult<()> {
187		self.hwnd().SetWindowText(text)?;
188		Ok(())
189	}
190
191	/// Displays a balloon tip by sending an
192	/// [`em::ShowBalloonTip`](crate::msg::em::ShowBalloonTip) message.
193	pub fn show_ballon_tip(&self, title: &str, text: &str, icon: co::TTI) -> SysResult<()> {
194		let mut title16 = WString::from_str(title);
195		let mut text16 = WString::from_str(text);
196
197		let mut info = EDITBALLOONTIP::default();
198		info.set_pszTitle(Some(&mut title16));
199		info.set_pszText(Some(&mut text16));
200		info.ttiIcon = icon;
201
202		unsafe { self.hwnd().SendMessage(em::ShowBalloonTip { info: &info }) }
203	}
204
205	/// Retrieves the text by calling
206	/// [`HWND::GetWindowText`](crate::HWND::GetWindowText).
207	#[must_use]
208	pub fn text(&self) -> SysResult<String> {
209		self.hwnd().GetWindowText()
210	}
211}
212
213/// Options to create an [`Edit`](crate::gui::Edit) programmatically with
214/// [`Edit::new`](crate::gui::Edit::new).
215pub struct EditOpts {
216	/// Text of the control to be
217	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
218	///
219	/// Defaults to empty string.
220	pub text: String,
221	/// Left and top position coordinates of control within parent's client
222	/// area, to be
223	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
224	///
225	/// Defaults to `gui::dpi(0, 0)`.
226	pub position: (i32, i32),
227	/// Control width to be
228	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
229	///
230	/// Defaults to `gui::dpi_x(100)`.
231	pub width: i32,
232	/// Control height to be
233	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
234	///
235	/// Defaults to `gui::dpi_y(23)`.
236	///
237	/// **Note:** You should change the default height only in a multi-line
238	/// edit, otherwise it will look off.
239	pub height: i32,
240	/// Edit styles to be
241	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
242	///
243	/// Defaults to `ES::AUTOHSCROLL | ES::NOHIDESEL`.
244	///
245	/// Suggestions:
246	/// * add `ES::PASSWORD` for a password input;
247	/// * add `ES::NUMBER` to accept only numbers;
248	/// * replace with `ES::MULTILINE | ES::WANTRETURN | ES::AUTOVSCROLL | ES::NOHIDESEL` for a multi-line edit.
249	pub control_style: co::ES,
250	/// Window styles to be
251	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
252	///
253	/// Defaults to `WS::CHILD | WS::GROUP | WS::TABSTOP | WS::VISIBLE`.
254	pub window_style: co::WS,
255	/// Extended window styles to be
256	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
257	///
258	/// Defaults to `WS_EX::LEFT | WS_EX::CLIENTEDGE`.
259	pub window_ex_style: co::WS_EX,
260
261	/// The control ID.
262	///
263	/// Defaults to an auto-generated ID.
264	pub ctrl_id: u16,
265	/// Horizontal and vertical behavior of the control when the parent window
266	/// is resized.
267	///
268	/// **Note:** You should use `Vert::Resize` only in a multi-line edit.
269	///
270	/// Defaults to `(gui::Horz::None, gui::Vert::None)`.
271	pub resize_behavior: (Horz, Vert),
272}
273
274impl Default for EditOpts {
275	fn default() -> Self {
276		Self {
277			text: "".to_owned(),
278			position: dpi(0, 0),
279			width: dpi_x(100),
280			height: dpi_y(23),
281			control_style: co::ES::AUTOHSCROLL | co::ES::NOHIDESEL,
282			window_style: co::WS::CHILD | co::WS::GROUP | co::WS::TABSTOP | co::WS::VISIBLE,
283			window_ex_style: co::WS_EX::LEFT | co::WS_EX::CLIENTEDGE,
284			ctrl_id: 0,
285			resize_behavior: (Horz::None, Vert::None),
286		}
287	}
288}